﻿using Common.JWT;
using Microsoft.Owin;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Infrastructure;
using Microsoft.Owin.Security.OAuth;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Security.Claims;
using System.Security.Principal;
using System.Threading.Tasks;

namespace OAuthServerApi
{
    public class GrantPwdAuthorizationServerProvider : OAuthAuthorizationServerProvider
    {
        public IJsonToken jsonToken = new JsonToken();
        public static readonly string signKey = ConfigurationManager.AppSettings["JwtSignKey"];

        #region Grant-Type
        /// <summary>
        /// 刷新令牌
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public override async Task GrantRefreshToken(OAuthGrantRefreshTokenContext context)
        {
            var oAuthIdentity = new ClaimsIdentity(context.Ticket.Identity);
            oAuthIdentity.AddClaim(new Claim(CustomClaimsType.GrantName, RequestGrantType.refresh_token.ToString()));
            oAuthIdentity.AddClaim(new Claim(CustomClaimsType.Now, DateTime.Now.Ticks.ToString()));
            var properties = new AuthenticationProperties()
            {
                IssuedUtc = DateTime.Now,
                ExpiresUtc = DateTime.Now.Add(context.Options.AccessTokenExpireTimeSpan)
            };
            var ticket = new AuthenticationTicket(oAuthIdentity, properties);
            context.Validated(ticket);
            await base.GrantRefreshToken(context);
        }

        /// <summary>
        /// 客户端验证模式
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public override async Task GrantClientCredentials(OAuthGrantClientCredentialsContext context)
        {
            var oAuthIdentity = new ClaimsIdentity(context.Options.AuthenticationType);
            oAuthIdentity.AddClaim(new Claim(CustomClaimsType.ClientId, context.ClientId));
            oAuthIdentity.AddClaim(new Claim(CustomClaimsType.GrantType, ((int)RequestGrantType.client_credentials).ToString()));
            var ticket = new AuthenticationTicket(oAuthIdentity, new AuthenticationProperties());
            context.Validated(ticket);
            await base.GrantClientCredentials(context);
        }

        /// <summary>
        /// 用户密码验证模式
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
        {
            if (!string.IsNullOrEmpty(context.UserName) && !string.IsNullOrEmpty(context.Password))
            {
                using (var db = new DbOAuthHelper())
                {
                    var user = await db.GetUser(context.UserName, JwtHelper.MD5(context.Password, 32));
                    if (user != null)
                    {
                        var oAuthIdentity = new ClaimsIdentity(context.Options.AuthenticationType);
                        oAuthIdentity.AddClaim(new Claim(CustomClaimsType.UserId, user.UserHash));
                        oAuthIdentity.AddClaim(new Claim(CustomClaimsType.GrantType, ((int)RequestGrantType.password).ToString()));
                        oAuthIdentity.AddClaim(new Claim(CustomClaimsType.ClientId, context.ClientId));
                        var ticket = new AuthenticationTicket(oAuthIdentity, new AuthenticationProperties());
                        context.Validated(ticket);
                        await base.GrantResourceOwnerCredentials(context);
                        return;
                    }
                }
            }
            context.SetError("invalid_grant", "The user name or password is incorrect.");
            await Task.FromResult<object>(null);
        }

        /// <summary>
        /// AuthorizationCode授权模式
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public override async Task GrantAuthorizationCode(OAuthGrantAuthorizationCodeContext context)
        {
            var oAuthIdentity = new ClaimsIdentity(context.Options.AuthenticationType);
            oAuthIdentity.AddClaim(new Claim(CustomClaimsType.GrantType, ((int)RequestGrantType.authorization_code).ToString()));
            //获取请求中的Code，Owin环境中对应数据库的值
            var code = context.OwinContext.Get<OAuthCode>(OwinEnvType.GrantCode);
            if (code == null)
            {
                context.SetError("invalid_grant_code");
                await Task.FromResult<object>(null);
                return;
            }
            oAuthIdentity.AddClaim(new Claim(CustomClaimsType.ClientId, code.ClientHash));
            oAuthIdentity.AddClaim(new Claim(CustomClaimsType.UserId, code.UserHash));
            oAuthIdentity.AddClaim(new Claim(CustomClaimsType.GrantCode, code.GrantCode));
            oAuthIdentity.AddClaim(new Claim(CustomClaimsType.ResponseType, code.ResponseType.ToString()));
            var ticket = new AuthenticationTicket(oAuthIdentity, new AuthenticationProperties());
            context.Validated(ticket);
            await base.GrantAuthorizationCode(context);
        }
        #endregion

        #region 验证Client信息
        /// <summary>
        /// 验证Client
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
        {
            string clientId;
            string clientSecret;
            context.TryGetFormCredentials(out clientId, out clientSecret);
            using (var db = new DbOAuthHelper())
            {
                var b = false;
                var client = await db.GetOAuthClient(clientId);
                if (client != null && client.ClientSecret == clientSecret)
                {
                    b = true;
                }
                if (!b)
                {
                    context.SetError("invalid_grant", "The client_id or client_secret is incorrect.");
                    await Task.FromResult<object>(null);
                    return;
                }
                context.Validated(clientId);
                await base.ValidateClientAuthentication(context);
            }
        }

        #region Implicit&授权模式
        /// <summary>
        /// 验证跳转url
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public override async Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context)
        {
            if (!string.IsNullOrEmpty(context.ClientId) && !string.IsNullOrEmpty(context.RedirectUri))
            {
                using (var db = new DbOAuthHelper())
                {
                    var client = await db.GetOAuthClient(context.ClientId);
                    if (client != null && client.ClientRedirectUri == context.RedirectUri)
                    {
                        context.Validated(context.RedirectUri);
                    }
                    else
                    {
                        //跳转error页面
                        context.SetError("invalid_client_uri");
                        await Task.FromResult<object>(null);
                        return;
                    }
                }
            }
            else
            {
                //跳转error页面
                await Task.FromResult<object>(null);
                return;
            }
        }

        #region 注释
        //public override async Task ValidateAuthorizeRequest(OAuthValidateAuthorizeRequestContext context)
        //{
        //    if (context.AuthorizeRequest.ClientId == "default" &&
        //   (context.AuthorizeRequest.IsAuthorizationCodeGrantType || context.AuthorizeRequest.IsImplicitGrantType))
        //    {
        //        context.Validated();
        //    }
        //    else
        //    {
        //        context.Rejected();
        //    }
        //    await Task.FromResult<object>(null);
        //    return;
        //}

        #endregion
        public override async Task AuthorizeEndpoint(OAuthAuthorizeEndpointContext context)
        {
            var redirectUri = context.Request.Query["redirect_uri"];
            var clientId = context.Request.Query["client_id"];
            var userLoginUrl = string.Empty;
            var userIdentityCookie = string.Empty;
            var userIdentity = new OAuthUserIdentity();
            var oAuthIdentity = new ClaimsIdentity(new GenericIdentity(clientId, context.Options.AuthenticationType));
            oAuthIdentity.AddClaim(new Claim(CustomClaimsType.ClientId, clientId));
            oAuthIdentity.AddClaim(new Claim(CustomClaimsType.RedirectUri, redirectUri));
            #region 判断cookie&设置ClaimsIdentity
            if (context.AuthorizeRequest.IsImplicitGrantType)
            {
                //获取cookie中的implicit_user
                userIdentityCookie = context.Request.Cookies[OwinCookieType.CookieImplicitUser];
                userLoginUrl = "/login.html?client_id=" + clientId + "&redirect_uri=" + Uri.EscapeDataString(redirectUri) + "&state=" + context.Request.Query["state"] + "&token=" + jsonToken.Encode(ResponseType.token.ToString(), signKey);
                if (userIdentityCookie == null)
                {
                    context.Response.Redirect(userLoginUrl);
                    context.RequestCompleted();
                    return;
                }
                userIdentity = jsonToken.Decode<OAuthUserIdentity>(userIdentityCookie, signKey);
                if (userIdentity == null)
                {
                    context.Response.Redirect(userLoginUrl);
                    context.RequestCompleted();
                    return;
                }
                oAuthIdentity.AddClaim(new Claim(CustomClaimsType.ResponseType, ((int)ResponseType.token).ToString()));
            }
            else if (context.AuthorizeRequest.IsAuthorizationCodeGrantType)
            {
                //获取cookie中的user_identity
                userIdentityCookie = context.Request.Cookies[OwinCookieType.CookieUserIdentity];
                userLoginUrl = "/login.html?client_id=" + clientId + "&redirect_uri=" + Uri.EscapeDataString(redirectUri) + "&state=" + context.Request.Query["state"] + "&token=" + jsonToken.Encode(ResponseType.code.ToString(), signKey); ;
                if (userIdentityCookie == null)
                {
                    context.Response.Redirect(userLoginUrl);
                    context.RequestCompleted();
                    return;
                }
                userIdentity = jsonToken.Decode<OAuthUserIdentity>(userIdentityCookie, signKey);
                if (userIdentity == null)
                {
                    context.Response.Redirect(userLoginUrl);
                    context.RequestCompleted();
                    return;
                }
                oAuthIdentity.AddClaim(new Claim(CustomClaimsType.ResponseType, ((int)ResponseType.code).ToString()));
            }
            else
            {
                await Task.FromResult<object>(null);
                return;
            }
            #endregion
            oAuthIdentity.AddClaim(new Claim(CustomClaimsType.UserId, userIdentity.UserHash));
            //设置票据Context
            var authorizeContext = new AuthenticationTokenCreateContext(
                 context.OwinContext,
                 context.Options.AuthorizationCodeFormat,
                 new AuthenticationTicket(
                     oAuthIdentity,
                     new AuthenticationProperties(new Dictionary<string, string>
                     {
                            {"client_id", clientId},
                            {"redirect_uri", redirectUri}
                     })
                     {
                         IssuedUtc = DateTime.Now,
                         ExpiresUtc = DateTime.Now.Add(context.AuthorizeRequest.IsImplicitGrantType ? context.Options.AccessTokenExpireTimeSpan : context.Options.AuthorizationCodeExpireTimeSpan)
                     }
                 )
            );
            #region 设置返回
            if (context.AuthorizeRequest.IsImplicitGrantType)
            {
                //implicit 授权方式
                await context.Options.AccessTokenProvider.CreateAsync(authorizeContext);
                //移除cookie中implicit_user
                context.Response.Cookies.Delete(OwinCookieType.CookieImplicitUser, new CookieOptions()
                {
                    Path = "/",
                    HttpOnly = true
                });
                context.Response.Redirect(redirectUri + "?access_token=" + Uri.EscapeDataString(authorizeContext.Token) + "&state=" + context.Request.Query["state"]);
                context.RequestCompleted();
            }
            else if (context.AuthorizeRequest.IsAuthorizationCodeGrantType)
            {
                //authorization code 授权方式
                await context.Options.AuthorizationCodeProvider.CreateAsync(authorizeContext);
                //移除cookie中user_identity
                context.Response.Cookies.Delete(OwinCookieType.CookieUserIdentity, new CookieOptions()
                {
                    Path = "/",
                    HttpOnly = true
                });
                context.Response.Redirect(redirectUri + "?code=" + Uri.EscapeDataString(authorizeContext.Token) + "&state=" + context.Request.Query["state"]);
                context.RequestCompleted();
            } 
            #endregion
        }
        #endregion

        #endregion

        public override async Task TokenEndpoint(OAuthTokenEndpointContext context)
        {
            context.AdditionalResponseParameters.Add("refresh_expires_in", AppConstNum.refresh_expires_second);
            await base.TokenEndpoint(context);
        }

    }
}